2022
Jan
01
这篇文章说明如果 Mock private Method ,帮助你减化 unit test 范围,进而提升写测试的意愿与让测试码更好维护。
假如你想测试 class 中的 Method A ,而这个 Method A 分别又去 call Method B / C ,为了降低测试的范围,我们会希望 Mock Method B 和 C 的 return 值 (mock 是指模拟物件的行为
)。
B / C 如果是 public 或是 protected method ,我们只要使用 mockito
or spock
就能轻松做 到 mock,困难的是如果想 mock 的对象是 private method , private method 只有 object 自已可以 access ,其它 test framework 都没办法对 private method 下手,目前唯有 PowerMock
,它直接修改了 java class byte-code ,把 private 改成 public accessible,让测试码能直接操作 private method 。
Example
- Method A
- - Call private Method B
- - Call private Method C
powerMock 使用范例
- 我有一个 class App.java,其中 log 是 public , logInternal 是我想 mock 的 private method
App.java
- package com;
- public class App {
- public App() {
- }
- public String log() {
- System.out.println("run log");
- return this.logInternal();
- }
- private String logInternal() {
- System.out.println("run logInternal");
- return "internal";
- }
- }
- class AppTest.java: 需要使用 PowerMock 中的 createPartialMock / expectPrivate / replay 这 3 个
AppTest.java
- package com;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import static org.junit.Assert.assertEquals;
- import org.powermock.api.easymock.PowerMock;
- import org.powermock.core.classloader.annotations.PrepareForTest;
- import org.powermock.modules.junit4.PowerMockRunner;
- @RunWith(PowerMockRunner.class)
- @PrepareForTest(App.class)
- public class AppTest {
- @Test
- public void TestLog() {
- try {
- App app = PowerMock.createPartialMock(App.class, "logInternal");
- PowerMock.expectPrivate(app, "logInternal").andReturn("mock_result");
- PowerMock.replay(app);
- String ret = app.log();
- assertEquals("Result is " + ret, ret, "mock_result");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- pom.xml dependency
Example
- <dependency>
- <groupId>org.powermock</groupId>
- <artifactId>powermock-module-junit4</artifactId>
- <version>2.0.9</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.powermock</groupId>
- <artifactId>powermock-api-mockito2</artifactId>
- <version>2.0.9</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.powermock</groupId>
- <artifactId>powermock-api-easymock</artifactId>
- <version>2.0.9</version>
- <scope>test</scope>
- </dependency>
build.gradle
使用 gradle 的话,build config 可以参考这份
Example
- plugins {
- id "application"
- id "java"
- }
- application {
- mainClassName = 'com.App'
- }
- allprojects {
- repositories {
- mavenCentral()
- }
- }
- dependencies {
- def powerMockVersion="2.0.9"
- implementation (
- "org.powermock:powermock-module-junit4:${powerMockVersion}",
- "org.powermock:powermock-api-mockito2:${powerMockVersion}",
- "org.powermock:powermock-api-easymock:${powerMockVersion}"
- )
- }